package com.googlecode.continuity.core.server.hibernate;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import net.sf.hibernate4gwt.rebind.JavassistProxyGenerator;
import net.sf.hibernate4gwt.rebind.ProxyGeneratorHelper;
import net.sf.hibernate4gwt.pojo.ILazyPojo;
import net.sf.hibernate4gwt.exception.ProxyException;
import javassist.*;

/**
 * UbiquitousJavassistProxyGenerator
 *
 * Class mostly copied from {@link net.sf.hibernate4gwt.rebind.JavassistProxyGenerator}.  Changed to use additional
 * classpaths to make it work in application servers such as Tomcat and JBoss.
 *
 * @author Peter Schmitt <aragos@aragos.de>
 */
public class UbiquitousJavassistProxyGenerator extends JavassistProxyGenerator {

  protected static Log log = LogFactory.getLog(JavassistProxyGenerator.class);

  public Class generateProxyFor(Class clazz) {
    try
		{
		//	Compute proxy class name
		//
			String sourceClassName = clazz.getName();
			String proxyClassName = ProxyGeneratorHelper.computeProxyName(sourceClassName);
			log.info("Generating server proxy " + proxyClassName + " for class " + sourceClassName);

		//	Create proxy class
		//
			ClassPool pool = ClassPool.getDefault();
      // added line
      pool.insertClassPath(new ClassClassPath(this.getClass()));
      
      CtClass proxyClass = pool.makeClass(proxyClassName);

		//	Add proxy inheritance
		//
			proxyClass.setSuperclass(pool.get(sourceClassName));

		//	Add ILazyPojo inheritance
		//
			proxyClass.addInterface(pool.get(ILazyPojo.class.getName()));

		//	TODO add lazy properties
		//
			generateILazyPojo(proxyClass);

		//	Generate class
		//
			return proxyClass.toClass(clazz.getClassLoader());
		}
		catch(Exception ex)
		{
			throw new ProxyException("Proxy generation failure for " + clazz.getName(), ex);
		}
  }

  /**
	 * Generates ILazyPojo classes and methods
	 * @throws javassist.CannotCompileException
	 */
	private void generateILazyPojo(CtClass proxyClass) throws CannotCompileException
	{
	//	Generate attribute
	//
		CtField lazyPropertiesFied = CtField.make("protected java.util.List _lazyProperties = new java.util.ArrayList();",
												  proxyClass);
		proxyClass.addField(lazyPropertiesFied);

	//	Generate methods
	//
		generateAddLazyProperty(proxyClass);
		generateIsLazyProperty(proxyClass);
	}

	/**
	 * Generate 'addLazyProperty' method to the instrumented class
	 * @param proxyClass
	 * @throws CannotCompileException
	 */
	private void generateAddLazyProperty(CtClass proxyClass) throws CannotCompileException
	{
	//	Source code
	//
		StringBuffer sourceCode = new StringBuffer();
		sourceCode.append("public void addLazyProperty(String property)");
		sourceCode.append("{ _lazyProperties.add(property); }");

	//	Add method body
	//
		CtMethod method = CtNewMethod.make(sourceCode.toString(), proxyClass);
		proxyClass.addMethod(method);

	}

	/**
	 * Generate 'isLazyProperty' method to the instrumented class
	 * @param proxyClass
	 * @throws CannotCompileException
	 */
	private void generateIsLazyProperty(CtClass proxyClass) throws CannotCompileException
	{
	//	Source code
	//
		StringBuffer sourceCode = new StringBuffer();
		sourceCode.append("public boolean isLazyProperty(String property)");
		sourceCode.append("{ return _lazyProperties.contains(property); }");

	//	Add method body
	//
		CtMethod method = CtNewMethod.make(sourceCode.toString(), proxyClass);
		proxyClass.addMethod(method);
	}
}
